package sw6.admin;

import java.io.IOException;

import sw6.admin.database.DatabaseHelper;
import sw6.admin.parser.ResetSettings;
import sw6.admin.parser.InsertSettings;
import sw6.admin.parser.SettingsParser;
import sw6.admin.parser.UpgradeSettings;
import sw6.lib.internal.PrivateDefinitions;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.util.Log;

/**
 * The purpose of this broadcast receiver is controlling the broadcasts 
 * there are being thrown from Android when there are different events with 
 * packages. This applies when the packages are installed, deleted,
 * updated, or gets deleted all internal data files. The class is made ​​
 * thus being called by the following broadcasts:
 * 
 * 		- android.intent.action.PACKAGE_ADDED
 * 		- android.intent.action.PACKAGE_REPLACED
 * 		- android.intent.action.PACKAGE_REMOVED
 * 		- android.intent.action.PACKAGE_DATA_CLEARED
 * 
 * We respond only to these broadcasts if it comes from an application 
 * that starts with "sw6." and if the sw6.admin database is initialized. 
 * If the database is not initialized, it will be when it is needed by 
 * request (hopefully a very rare case).
 * 
 * @author sw6b
 */
public class PackageHandler extends BroadcastReceiver {
	
	private static final String LOG_NAME = "sw6.admin.PackageHandler";

	@Override
	public void onReceive(Context context, Intent intent) {
		// Name of the application there are beeing installed/updated/uninstalled/cleared
		String appName = intent.getData().getSchemeSpecificPart();

		/**
		 * We are only interested in packages beginning with sw6.* 
		 * and when the database for sw6.admin is created.
		 */
		if (appName.startsWith(PrivateDefinitions.GIRAF_APP_PREFIX) && DatabaseHelper.checkDataBaseExsists(context)) {
			
			/* [from the Android documentation]
			 * 
			 * A new application package has been installed on the device. 
			 * The data contains the name of the package. Note that the 
			 * newly installed package does not receive this broadcast.
			 */
			if (intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)) {
				Log.i(LOG_NAME, "Received Intent.ACTION_PACKAGE_ADDED");
				
				/**
				 *  Checking if the new package gonna replace another package.
				 *  If this is true we should not do anything here because we
				 *  know that Android will call our PackageHandler again with
				 *  the ACTION_PACKAGE_REPLACED intent.
				 */
				if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
					try {
						/**
						 * Parsing settings.xml for packageName (if exists). 
						 * Every setting in the file is placed into the database.
						 */
						SettingsParser.parse(context, appName, new InsertSettings());
						
					} catch (NameNotFoundException e) {
						/**
						 * This is not always a bug but we expect all applications packages 
						 * that begin with "sw6." has a settings.xml file. If not we got
						 * this exception.
						 */
						Log.e(LOG_NAME, "Could not find " + PrivateDefinitions.SETTINGS_FILE_NAME + 
								" for package: " + appName + ". This is not a bug if the app don't provide a file with the name" + 
								PrivateDefinitions.SETTINGS_FILE_NAME + ".");
						e.printStackTrace();
					} catch (IOException e) {
						Log.e(LOG_NAME, "Some IOException for package: " + appName);
						e.printStackTrace();
					}
				} else {
					/**
					 * Because of the way Android upgrade packages we will receive 3 broadcasts:
					 * 
					 * 	- Uninstalling the package
					 * 	- Installing the package
					 * 	- Upgrading the package
					 * 
					 * For the first 2 broadcasts Android sends a boolean with whether 
					 * it is an upgrade that is performed. If this boolean is true we 
					 * know the upgradring broadcast is gonna be called later and we 
					 * will just ignore this broadcast.
					 */
					//Log.i(LOG_NAME, appName + " has replacing bit set to true. Coming back later.");
				}
			}
			
			/* [from the Android documentation]
			 * 
			 * A new version of an application package has been installed, 
			 * replacing an existing version that was previously installed. 
			 * The data contains the name of the package. 
			 */
			else if (intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)) {
				Log.i(LOG_NAME, "Received Intent.ACTION_PACKAGE_REPLACED");		
				try {
					/*
					 * Parsing settings.xml and make the changes in the database.
					 * 
					 * The rule is summarized that if a setting in the database 
					 * are valid in relation to settings.xml we let the setting 
					 * be in the database. If the setting in the database however 
					 * is against the rules which are written in settings.xml we 
					 * will reset option under the settings.xml.
					 * 
					 * Options that do not appear in settings.xml will be deleted 
					 * and missing settings in the database will be added with 
					 * default values​​ according to settings.xml.
					 */
					SettingsParser.parse(context, appName, new UpgradeSettings());
					
				} catch (NameNotFoundException e) {
					/* 
					 * Comment about this exception can be found 
					 * in the code there handle ACTION_PACKAGE_ADDED.
					 * (also in this file).
					 */
					Log.e(LOG_NAME, "Could not find " + PrivateDefinitions.SETTINGS_FILE_NAME + 
							" for package: " + appName + ". This is not a bug if the app don't provide a file with the name" + 
							PrivateDefinitions.SETTINGS_FILE_NAME + ".");
					e.printStackTrace();
				} catch (IOException e) {
					Log.e(LOG_NAME, "Some IOException for package: " + appName);
					e.printStackTrace();
				}
			}
			

			/** [from the Android documentation]
			 * 
			 * An existing application package has been removed from the device. 
			 * The data contains the name of the package. The package that is 
			 * being installed does not receive this Intent.
			 */
			else if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)) {
				Log.i(LOG_NAME, "Received Intent.ACTION_PACKAGE_REMOVED");
				
				// See the comment about this check in the if-statement for ACTION_PACKAGE_ADDED
				if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
					/**
					 * We delete all variables in the database belonging 
					 * to the deleted application.
					 */
					Log.i(LOG_NAME, "Delete settings for: " + appName);
					new DatabaseHelper(context).deleteApp(appName);
					
				} else {
					/**
					 * See the comment about this in the code for Intent.ACTION_PACKAGE_ADDED
					 */
					//Log.i(LOG_NAME, appName + " has replacing bit set to true. Coming back later.");
				}
			} 
			
			/* [from the Android documentation]
			 * 
			 * The user has cleared the data of a package. This should be preceded 
			 * by ACTION_PACKAGE_RESTARTED, after which all of its persistent data 
			 * is erased and this broadcast sent. Note that the cleared package does 
			 * not receive this broadcast. The data contains the name of the package.
			 */
			else if (intent.getAction().equals(Intent.ACTION_PACKAGE_DATA_CLEARED)) {
				Log.i(LOG_NAME, "Received Intent.ACTION_PACKAGE_DATA_CLEARED");
				try {
					/**
					 * The user has chosen to reset all data. We must therefore 
					 * ensure that it also happens in the database (because it
					 * would be strange if the application still have some
					 * settings saved).
					 * 
					 * To reset the settings we parse settings.xml for the package 
					 * and reinsert all the default settings described in settings.xml.
					 */
					SettingsParser.parse(context, appName, new ResetSettings());
					
				} catch (NameNotFoundException e) {
					/* 
					 * Comment about this exception can be found 
					 * in the code there handle ACTION_PACKAGE_ADDED.
					 * (also in this file).
					 */
					Log.e(LOG_NAME, "Could not find " + PrivateDefinitions.SETTINGS_FILE_NAME + 
							" for package: " + appName + ". This is not a bug if the app don't provide a file with the name" + 
							PrivateDefinitions.SETTINGS_FILE_NAME + ".");
					e.printStackTrace();
				} catch (IOException e) {
					Log.e(LOG_NAME, "Some IOException for package: " + appName);
					e.printStackTrace();
				}
			} 
			
			// This should never happen so if this information is going to the log please tell us
			else {
				Log.d(LOG_NAME, "Can't recognize this action: " + intent.getAction());
			}
		}
	}
}
